<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>RAG 端到端评测平台 (专业版)</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', 'Helvetica Neue', 'Arial', 'sans-serif';
        }
        .step-card {
            background-color: white;
            border-radius: 0.75rem;
            border: 1px solid #e5e7eb;
            padding: 1.5rem;
            margin-bottom: 1.5rem;
            box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.05), 0 1px 2px -1px rgb(0 0 0 / 0.05);
            transition: all 0.3s ease-in-out;
        }
        .step-header {
            display: flex;
            align-items: center;
            margin-bottom: 1rem;
        }
        .step-number {
            background-color: #4f46e5;
            color: white;
            border-radius: 9999px;
            width: 2rem;
            height: 2rem;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: 600;
            margin-right: 0.75rem;
        }
        .step-title {
            font-size: 1.125rem;
            font-weight: 600;
            color: #111827;
        }
        .result-card {
            background-color: #f9fafb;
            border: 1px solid #e5e7eb;
            border-radius: 0.5rem;
            padding: 1rem;
            margin-top: 1rem;
        }
        .loader {
            border: 4px solid #f3f3f3;
            border-top: 4px solid #4f46e5;
            border-radius: 50%;
            width: 24px;
            height: 24px;
            animation: spin 1s linear infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        .source-tag {
            font-size: 0.7rem;
            padding: 0.1rem 0.4rem;
            border-radius: 0.25rem;
            font-weight: 600;
            margin-left: 0.5rem;
        }
    </style>
</head>
<body class="bg-gray-50 text-gray-800">

    <div class="container mx-auto p-4 md:p-8">
        <header class="text-center mb-10">
            <h1 class="text-3xl md:text-4xl font-bold text-gray-900">RAG 端到端评测平台 (专业版)</h1>
            <p class="mt-2 text-md text-gray-600">一个用于评估和对比RAG系统各环节性能的交互式工具</p>
        </header>

        <div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
            <!-- 配置面板 (左侧) -->
            <aside class="lg:col-span-4">
                <!-- 步骤一：问题与知识库 -->
                <div class="step-card">
                    <div class="step-header">
                        <div class="step-number">1</div>
                        <h2 class="step-title">输入问题</h2>
                    </div>
                    <div class="space-y-4">
                        <div>
                            <label for="question" class="block text-sm font-medium text-gray-700">用户问题</label>
                            <textarea id="question" rows="3" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" placeholder="例如：公司今年的年假政策是什么？">公司今年的年假政策是什么？</textarea>
                        </div>
                        <div>
                            <label for="knowledge-base" class="block text-sm font-medium text-gray-700">知识库范围</label>
                            <select id="knowledge-base" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
                                <option>人力资源政策库</option>
                                <option>财务报销制度库</option>
                                <option>IT支持文档库</option>
                            </select>
                        </div>
                    </div>
                </div>

                <!-- 步骤二：召回 (Retrieval) -->
                <div class="step-card">
                    <div class="step-header">
                        <div class="step-number">2</div>
                        <h2 class="step-title">召回 (Retrieval)</h2>
                    </div>
                    <div class="space-y-4">
                        <div>
                            <label for="retrieval-strategy" class="block text-sm font-medium text-gray-700">召回策略</label>
                            <select id="retrieval-strategy" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
                                <option value="hybrid">混合检索</option>
                                <option value="vector">向量检索</option>
                                <option value="keyword">关键词检索</option>
                            </select>
                        </div>
                        <div>
                            <label for="embedding-model" class="block text-sm font-medium text-gray-700">向量化模型 (仅向量/混合检索时生效)</label>
                            <select id="embedding-model" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
                                <option>BGE-large-zh-v1.5</option>
                                <option>M3E-large</option>
                                <option>text2vec-large-chinese</option>
                            </select>
                        </div>
                        <div>
                            <label for="recall-topk" class="block text-sm font-medium text-gray-700">召回 Top K</label>
                            <input type="number" id="recall-topk" value="10" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
                        </div>
                        <button id="recall-btn" class="w-full bg-indigo-600 text-white font-semibold py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 flex items-center justify-center">
                            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="m21 21-4.3-4.3"/><circle cx="11" cy="11" r="8"/></svg>
                            执行召回
                        </button>
                    </div>
                </div>

                <!-- 步骤三：重排 (Rerank) -->
                <div class="step-card">
                    <div class="step-header">
                        <div class="step-number">3</div>
                        <h2 class="step-title">重排 (Rerank)</h2>
                    </div>
                    <div class="space-y-4">
                        <div>
                            <label for="rerank-model" class="block text-sm font-medium text-gray-700">重排模型</label>
                            <select id="rerank-model" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
                                <option>BGE-Reranker-large</option>
                                <option>Qwen3_4B-Reranker</option>
                                <option>不使用重排</option>
                            </select>
                        </div>
                        <div>
                            <label for="rerank-topk" class="block text-sm font-medium text-gray-700">重排 Top K</label>
                            <input type="number" id="rerank-topk" value="5" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
                        </div>
                        <button id="rerank-btn" class="w-full bg-indigo-600 text-white font-semibold py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 flex items-center justify-center">
                            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M3 6h18"/><path d="M7 12h10"/><path d="M10 18h4"/></svg>
                            执行重排
                        </button>
                    </div>
                </div>
                
                <!-- 步骤四：生成 (Generate) -->
                <div class="step-card">
                    <div class="step-header">
                        <div class="step-number">4</div>
                        <h2 class="step-title">生成答案</h2>
                    </div>
                    <div class="space-y-4">
                        <div>
                            <label for="prompt-template" class="block text-sm font-medium text-gray-700">提示词 (Prompt)</label>
                            <textarea id="prompt-template" rows="4" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" placeholder="请根据以下背景知识，简洁、准确地回答用户的问题。...">请根据以下背景知识，简洁、准确地回答用户的问题。不允许回答背景知识中未提及的信息。

【背景知识】:
{context}

【用户问题】:
{question}

【你的回答】:</textarea>
                        </div>
                        <div>
                            <label class="block text-sm font-medium text-gray-700">选择对比的大模型</label>
                            <div class="mt-2 space-y-2">
                                <label class="flex items-center"><input type="checkbox" class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 llm-checkbox" value="GPT-4o" checked> <span class="ml-2 text-sm">GPT-4o</span></label>
                                <label class="flex items-center"><input type="checkbox" class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 llm-checkbox" value="Qwen-Max" checked> <span class="ml-2 text-sm">通义千问-Max</span></label>
                                <label class="flex items-center"><input type="checkbox" class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 llm-checkbox" value="Claude 3 Opus"> <span class="ml-2 text-sm">Claude 3 Opus</span></label>
                            </div>
                        </div>
                         <button id="generate-btn" class="w-full bg-emerald-600 text-white font-semibold py-2 px-4 rounded-md hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-emerald-500 flex items-center justify-center">
                            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M12 3c7.2 0 9 1.8 9 9s-1.8 9-9 9a9 9 0 0 1-9-9c0-7.2 1.8-9 9-9z"/><path d="M8 12h8"/><path d="M12 8v8"/></svg>
                            生成并对比答案
                        </button>
                    </div>
                </div>
            </aside>

            <!-- 结果面板 (右侧) -->
            <main class="lg:col-span-8">
                <!-- 召回结果 -->
                <div id="recall-results-section" class="step-card hidden">
                    <div class="flex justify-between items-center mb-4">
                        <h3 class="text-xl font-semibold text-gray-800">召回结果</h3>
                        <div class="flex items-center space-x-2">
                            <button id="ai-score-btn" class="bg-white text-indigo-600 border border-indigo-600 font-semibold py-2 px-4 rounded-md hover:bg-indigo-50 text-sm flex items-center">
                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
                                AI 黄金标准打分
                            </button>
                            <button id="evaluate-recall-btn" disabled class="bg-indigo-500 text-white font-semibold py-2 px-4 rounded-md hover:bg-indigo-600 text-sm flex items-center opacity-50 cursor-not-allowed">
                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M3 3v18h18"/><path d="M18.7 8a6 6 0 0 0-8.4 0L4 18"/><path d="M12 12v6h6"/></svg>
                                评估召回质量
                            </button>
                        </div>
                    </div>
                    <div id="recall-metrics" class="result-card hidden"></div>
                    <div id="recall-list" class="mt-4 space-y-3"></div>
                </div>

                <!-- 重排结果 -->
                <div id="rerank-results-section" class="step-card hidden">
                     <div class="flex justify-between items-center mb-4">
                        <h3 class="text-xl font-semibold text-gray-800">重排结果</h3>
                        <button id="evaluate-rerank-btn" disabled class="bg-indigo-500 text-white font-semibold py-2 px-4 rounded-md hover:bg-indigo-600 text-sm flex items-center opacity-50 cursor-not-allowed">
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M3 3v18h18"/><path d="M18.7 8a6 6 0 0 0-8.4 0L4 18"/><path d="M12 12v6h6"/></svg>
                            评估重排质量
                        </button>
                    </div>
                    <div id="rerank-metrics" class="result-card hidden"></div>
                    <div id="rerank-list" class="mt-4 space-y-3"></div>
                </div>

                <!-- 生成结果 -->
                <div id="generation-results-section" class="step-card hidden">
                    <h3 class="text-xl font-semibold text-gray-800 mb-4">大模型生成结果对比</h3>
                    <div id="generation-list" class="grid grid-cols-1 md:grid-cols-2 gap-6"></div>
                </div>
                
                <!-- 初始提示 -->
                <div id="initial-prompt" class="flex flex-col items-center justify-center h-full bg-white rounded-lg border-2 border-dashed border-gray-300 p-12 text-center">
                    <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
                        <path vector-effect="non-scaling-stroke" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z" />
                    </svg>
                    <h3 class="mt-2 text-lg font-medium text-gray-900">结果展示区</h3>
                    <p class="mt-1 text-sm text-gray-500">请在左侧面板完成配置并执行操作，结果将在此处显示。</p>
                </div>

            </main>
        </div>
    </div>

    <script>
        // --- DOM Elements ---
        const recallBtn = document.getElementById('recall-btn');
        const rerankBtn = document.getElementById('rerank-btn');
        const aiScoreBtn = document.getElementById('ai-score-btn');
        const evaluateRecallBtn = document.getElementById('evaluate-recall-btn');
        const evaluateRerankBtn = document.getElementById('evaluate-rerank-btn');
        const generateBtn = document.getElementById('generate-btn');

        const initialPrompt = document.getElementById('initial-prompt');
        const recallResultsSection = document.getElementById('recall-results-section');
        const rerankResultsSection = document.getElementById('rerank-results-section');
        const generationResultsSection = document.getElementById('generation-results-section');

        const recallList = document.getElementById('recall-list');
        const rerankList = document.getElementById('rerank-list');
        const generationList = document.getElementById('generation-list');
        
        const recallMetrics = document.getElementById('recall-metrics');
        const rerankMetrics = document.getElementById('rerank-metrics');
        
        const retrievalStrategySelect = document.getElementById('retrieval-strategy');
        const embeddingModelSelect = document.getElementById('embedding-model');

        // --- Mock Data & State ---
        const mockRecallData = [
            { id: 'doc-03', content: '关于年假，所有入职满一年的正式员工享有10天带薪年假。', source: 'both' },
            { id: 'doc-08', content: '员工的年假天数会根据服务年限进行调整，每满五年增加一天。', source: 'vector' },
            { id: 'doc-01', content: '公司的标准工作时间为周一至周五，上午9点至下午6点。', source: 'keyword' },
            { id: 'doc-05', content: '新员工的试用期为三个月，试用期内不享有年假。', source: 'vector' },
            { id: 'doc-12', content: '年假申请需要提前两周在HR系统提交，由直属上级和HR部门审批。', source: 'both' },
            { id: 'doc-02', content: '加班餐补标准为30元/人，需在晚上8点后申请。', source: 'keyword' },
            { id: 'doc-09', content: '年度体检为公司福利，所有员工均可参加。', source: 'keyword' },
            { id: 'doc-15', content: '差旅报销需提供有效发票，并在行程结束后一周内提交。', source: 'vector' },
            { id: 'doc-04', content: '员工生日会于每月最后一个周五举行。', source: 'keyword' },
            { id: 'doc-07', content: '未休年假可在年底申请折算为三倍工资。', source: 'vector' }
        ];

        const mockGoldenScores = { 'doc-03': 0.95, 'doc-08': 0.88, 'doc-01': 0.10, 'doc-05': 0.75, 'doc-12': 0.92, 'doc-02': 0.05, 'doc-09': 0.02, 'doc-15': 0.01, 'doc-04': 0.01, 'doc-07': 0.98 };
        
        let currentRecallData = [];
        let currentRerankData = []; // Store reranked data with scores
        let goldenScoresAvailable = false;

        // --- Functions ---
        function showLoader(button) {
            button.disabled = true;
            button.innerHTML = `<div class="loader"></div> <span class="ml-2">处理中...</span>`;
        }

        function hideLoader(button, originalContent) {
            button.disabled = false;
            button.innerHTML = originalContent;
        }
        
        // UPDATED: Function now accepts rerankScore
        function createDocumentItem(doc, index, goldenScore = null, rerankScore = null) {
            // Visualize Retrieval Source (only for recall list)
            let sourceTagHtml = '';
            if (rerankScore === null && retrievalStrategySelect.value === 'hybrid' && doc.source) {
                const tagMap = {
                    'vector': { text: 'V', color: 'bg-purple-100 text-purple-800', title: '向量检索来源' },
                    'keyword': { text: 'K', color: 'bg-blue-100 text-blue-800', title: '关键词检索来源' },
                    'both': { text: 'V+K', color: 'bg-green-100 text-green-800', title: '向量与关键词共同来源' }
                };
                const tag = tagMap[doc.source];
                if (tag) {
                    sourceTagHtml = `<span class="source-tag ${tag.color}" title="${tag.title}">${tag.text}</span>`;
                }
            }
            
            // Display scores
            let scoreHtml = '';
            if (goldenScore !== null) {
                const goldenScoreColor = goldenScore > 0.8 ? 'text-green-600' : goldenScore > 0.5 ? 'text-yellow-600' : 'text-red-600';
                scoreHtml += `
                    <div class="text-xs text-gray-500">相关性</div>
                    <div class="font-bold ${goldenScoreColor}">${goldenScore.toFixed(2)}</div>
                `;
            }
            if (rerankScore !== null) {
                const rerankScoreColor = rerankScore > 0.8 ? 'text-blue-600' : rerankScore > 0.5 ? 'text-cyan-600' : 'text-gray-500';
                scoreHtml += `
                    <div class="text-xs text-gray-500 mt-1">重排分</div>
                    <div class="font-bold ${rerankScoreColor}">${rerankScore.toFixed(3)}</div>
                `;
            }
            if (scoreHtml === '') {
                scoreHtml = `<div class="score-placeholder h-5"></div>`;
            }

            return `
                <div class="result-card flex items-start space-x-4">
                    <div class="flex-shrink-0 h-6 w-6 rounded-full bg-gray-200 text-gray-600 flex items-center justify-center text-sm font-bold">${index + 1}</div>
                    <div class="flex-grow">
                        <p class="text-sm text-gray-700 flex items-center">${doc.content} ${sourceTagHtml}</p>
                        <p class="text-xs text-gray-400 mt-1">文档ID: ${doc.id}</p>
                    </div>
                    <div class="flex-shrink-0 w-24 text-right">
                        ${scoreHtml}
                    </div>
                </div>
            `;
        }

        function displayMetrics(container, title, metrics) {
            let metricsHtml = `<h4 class="font-semibold text-md mb-2">${title}</h4><div class="grid grid-cols-2 md:grid-cols-3 gap-4 text-center">`;
            for (const [key, value] of Object.entries(metrics)) {
                metricsHtml += `
                    <div>
                        <div class="text-2xl font-bold text-indigo-600">${value}</div>
                        <div class="text-sm text-gray-500">${key}</div>
                    </div>
                `;
            }
            metricsHtml += `</div>`;
            container.innerHTML = metricsHtml;
            container.classList.remove('hidden');
        }

        function toggleEmbeddingModel() {
            const strategy = retrievalStrategySelect.value;
            if (strategy === 'keyword') {
                embeddingModelSelect.disabled = true;
                embeddingModelSelect.parentElement.classList.add('opacity-50');
            } else {
                embeddingModelSelect.disabled = false;
                embeddingModelSelect.parentElement.classList.remove('opacity-50');
            }
        }
        
        function resetEvalButtons() {
            evaluateRecallBtn.disabled = true;
            evaluateRecallBtn.classList.add('opacity-50', 'cursor-not-allowed');
            evaluateRerankBtn.disabled = true;
            evaluateRerankBtn.classList.add('opacity-50', 'cursor-not-allowed');
        }

        // --- Event Listeners ---
        retrievalStrategySelect.addEventListener('change', toggleEmbeddingModel);
        
        recallBtn.addEventListener('click', () => {
            const originalContent = recallBtn.innerHTML;
            showLoader(recallBtn);
            
            setTimeout(() => {
                initialPrompt.classList.add('hidden');
                recallResultsSection.classList.remove('hidden');
                rerankResultsSection.classList.add('hidden');
                generationResultsSection.classList.add('hidden');
                recallMetrics.classList.add('hidden');
                rerankMetrics.classList.add('hidden');
                rerankList.innerHTML = '';
                goldenScoresAvailable = false;
                resetEvalButtons();

                currentRecallData = [...mockRecallData].sort(() => 0.5 - Math.random()).slice(0, document.getElementById('recall-topk').value || 10);
                recallList.innerHTML = currentRecallData.map((doc, i) => createDocumentItem(doc, i, null, null)).join('');
                
                hideLoader(recallBtn, originalContent);
            }, 1500);
        });

        aiScoreBtn.addEventListener('click', () => {
            if (currentRecallData.length === 0) {
                alert('请先执行召回操作。');
                return;
            }
            const originalContent = aiScoreBtn.innerHTML;
            showLoader(aiScoreBtn);
            
            setTimeout(() => {
                recallList.innerHTML = currentRecallData.map((doc, i) => createDocumentItem(doc, i, mockGoldenScores[doc.id], null)).join('');
                goldenScoresAvailable = true;
                
                evaluateRecallBtn.disabled = false;
                evaluateRecallBtn.classList.remove('opacity-50', 'cursor-not-allowed');
                if (rerankList.innerHTML.trim() !== '') {
                    evaluateRerankBtn.disabled = false;
                    evaluateRerankBtn.classList.remove('opacity-50', 'cursor-not-allowed');
                }

                if (currentRerankData.length > 0) {
                     rerankList.innerHTML = currentRerankData.map((doc, i) => createDocumentItem(doc, i, mockGoldenScores[doc.id], doc.rerankScore)).join('');
                }
                hideLoader(aiScoreBtn, originalContent);
            }, 1000);
        });

        evaluateRecallBtn.addEventListener('click', () => {
            if (!goldenScoresAvailable) {
                alert('请先点击“AI 黄金标准打分”获取相关性分数后再进行评估。');
                return;
            }
            displayMetrics(recallMetrics, '召回质量评估', { 'Recall@10': '83.3%', '平均相关性@10': '0.57', 'MRR': '0.95' });
        });

        rerankBtn.addEventListener('click', () => {
            if (currentRecallData.length === 0) {
                alert('请先执行召回操作。');
                return;
            }
            
            const originalContent = rerankBtn.innerHTML;
            showLoader(rerankBtn);

            setTimeout(() => {
                rerankResultsSection.classList.remove('hidden');
                generationResultsSection.classList.add('hidden');
                rerankMetrics.classList.add('hidden');

                const rerankedData = [...currentRecallData].sort((a, b) => (mockGoldenScores[b.id] || 0) - (mockGoldenScores[a.id] || 0));
                const topK = document.getElementById('rerank-topk').value || 5;
                
                // Store reranked data with simulated scores
                currentRerankData = rerankedData.slice(0, topK).map(doc => ({
                    ...doc,
                    rerankScore: Math.random() * (0.999 - 0.850) + 0.850 // Simulate a reranker score
                }));

                rerankList.innerHTML = currentRerankData.map((doc, i) => createDocumentItem(doc, i, goldenScoresAvailable ? mockGoldenScores[doc.id] : null, doc.rerankScore)).join('');
                
                if(goldenScoresAvailable){
                    evaluateRerankBtn.disabled = false;
                    evaluateRerankBtn.classList.remove('opacity-50', 'cursor-not-allowed');
                }

                hideLoader(rerankBtn, originalContent);
            }, 1500);
        });

        evaluateRerankBtn.addEventListener('click', () => {
             if (rerankList.innerHTML.trim() === '') {
                alert('请先点击“执行重排”按钮。');
                return;
            }
            if (!goldenScoresAvailable) {
                alert('请先点击“AI 黄金标准打分”获取相关性分数后再进行评估。');
                return;
            }
            displayMetrics(rerankMetrics, '重排质量评估', { 'NDCG@5': '0.98', '命中率@5': '100%', '平均相关性@5': '0.92' });
        });
        
        generateBtn.addEventListener('click', () => {
            if (rerankList.innerHTML.trim() === '') {
                alert('请先执行召回和重排，以确定用于生成的上下文。');
                return;
            }
            
            let context = '';
            currentRerankData.forEach((doc, index) => {
                context += `[知识块 ${index + 1}]: ${doc.content}\n`;
            });
            
            const originalContent = generateBtn.innerHTML;
            showLoader(generateBtn);
            
            generationResultsSection.classList.remove('hidden');
            generationList.innerHTML = '';
            
            const selectedLLMs = Array.from(document.querySelectorAll('.llm-checkbox:checked')).map(el => el.value);

            if (selectedLLMs.length === 0) {
                alert('请至少选择一个大模型进行对比。');
                hideLoader(generateBtn, originalContent);
                return;
            }

            console.log("Context for Generation:\n", context);

            setTimeout(() => {
                const mockAnswers = {
                    'GPT-4o': `根据提供的 ${currentRerankData.length} 个知识块，公司规定入职满一年的正式员工享有10天带薪年假。`,
                    'Qwen-Max': `您好，综合所选知识，年假政策是：正式员工满一年后，可享受10天带薪年假。`,
                    'Claude 3 Opus': `分析上下文可知：年假的核心政策为10天，前提是入职满一年的正式员工。`
                };

                let generationHtml = '';
                selectedLLMs.forEach(llm => {
                    generationHtml += `
                        <div class="result-card bg-white">
                            <h4 class="font-semibold text-lg mb-3 text-indigo-700">${llm}</h4>
                            <div class="text-sm space-y-2 text-gray-800 leading-relaxed">
                                <p>${mockAnswers[llm] || '模拟生成中发生错误...'}</p>
                            </div>
                            <div class="mt-4 pt-3 border-t border-gray-200">
                                <h5 class="text-xs font-bold text-gray-500 mb-2">答案质量评估 (模拟)</h5>
                                <div class="flex justify-around text-center text-xs">
                                    <div><span class="font-semibold text-green-700">0.98</span><br>忠实度</div>
                                    <div><span class="font-semibold text-green-700">0.95</span><br>相关性</div>
                                    <div><span class="font-semibold text-green-700">0.90</span><br>简洁度</div>
                                </div>
                            </div>
                        </div>
                    `;
                });
                generationList.innerHTML = generationHtml;
                hideLoader(generateBtn, originalContent);
            }, 2000);
        });

        // Initial setup
        toggleEmbeddingModel();
    </script>
</body>
</html>
